int
xasprintf(char **strp, const char *fmt, ...)
{
+ /* From http://perfec.to/vsnprintf/pasprintf.c */
+/* size of first buffer malloc; start small to exercise grow routines */
+#define FIRSTSIZE 64
va_list args;
- int res;
-
- va_start(args, fmt);
- res = vsnprintf((char *)NULL, 0, fmt, args);
- *strp = xmalloc(res + 1);
- va_end(args);
-
- va_start(args, fmt);
- res = vsnprintf(*strp, res + 1, fmt, args);
- va_end(args);
+ char *buf;
+ size_t bufsize;
+ char *newbuf;
+ size_t nextsize;
+ int outsize;
+
+ bufsize = 0;
+ for (;;) {
+ if (bufsize == 0) {
+ if ((buf = xmalloc(FIRSTSIZE)) == NULL) {
+ *strp = NULL;
+ return -1;
+ }
+ bufsize = 1;
+ } else if ((newbuf = xrealloc(buf, nextsize)) != NULL) {
+ buf = newbuf;
+ bufsize = nextsize;
+ } else {
+ xfree(buf);
+ *strp = NULL;
+ return -1;
+ }
- is_fatal(res < 0, "(internal): vsnprintf returned %d!", res);
-
- return res;
+ va_start(args, fmt);
+ outsize = vsnprintf(buf, bufsize, fmt, args);
+ va_end(args);
+
+ if (outsize == -1) {
+ /* Clear indication that output was truncated, but no
+ * clear indication of how big buffer needs to be, so
+ * simply double existing buffer size for next time.
+ */
+ nextsize = bufsize * 2;
+
+ } else if (outsize == bufsize) {
+ /* Output was truncated (since at least the \0 could
+ * not fit), but no indication of how big the buffer
+ * needs to be, so just double existing buffer size
+ * for next time.
+ */
+ nextsize = bufsize * 2;
+
+ } else if (outsize > bufsize) {
+ /* Output was truncated, but we were told exactly how
+ * big the buffer needs to be next time. Add two chars
+ * to the returned size. One for the \0, and one to
+ * prevent ambiguity in the next case below.
+ */
+ nextsize = outsize + 2;
+
+ } else if (outsize == bufsize - 1) {
+ /* This is ambiguous. May mean that the output string
+ * exactly fits, but on some systems the output string
+ * may have been trucated. We can't tell.
+ * Just double the buffer size for next time.
+ */
+ nextsize = bufsize * 2;
+
+ } else {
+ /* Output was not truncated */
+ break;
+ }
+ }
+ *strp = buf;
+ return 0;
}
/*